home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d17 / lptx601.arc / LPTX.ASM < prev    next >
Assembly Source File  |  1987-04-08  |  47KB  |  1,661 lines

  1. title LPTx : Line PrinTer Output Capture Routine
  2. page    60,132
  3. ;------------------------------------------------------------
  4. ;
  5. ;    MAIN PROGRAM    Version 6.00
  6. ;
  7. ;  (C)    Copyright 1987 by Mark DiVecchio, All Rights Reserved
  8. ;
  9. .xlist
  10. ;
  11. ;This program is released for use in non-commercial environments. I
  12. ;ask commercial users to register the program with a $25 copyright fee for
  13. ;each site (any number of users and computers) at which the program is used.
  14. ;
  15. ; DISCLAMER : this program tries to perform a function which is
  16. ; not supported by DOS. It will work sometime and will not work
  17. ; other times. That kind of explains why you don't see this type
  18. ; of program on the market.
  19. ; I have tested it under DOS 2.1 and DOS 3.1. Some users have
  20. ; reported trouble when running under DOS 3.x and for other users
  21. ; it works fine. USE AT YOUR OWN RISK.
  22. ;
  23. ; Mark C. DiVecchio
  24. ; 10435 Mountain Glen Terrace
  25. ; San Diego, CA 92131
  26. ; 619-566-6810
  27. ;------------------------------------------------------------
  28. ; Updates for Version 6.00        18 Mar 87
  29. ;
  30. ; Added use of Timer Interrupt and Idle interrupt to permit
  31. ; writing to disk
  32. ;
  33. ; Added a switch to inhibit the output of linefeed characters when
  34. ; capturing a file. Program strips linefeed character at the
  35. ; end of the line if you turn on this switch. The switch is -l on the
  36. ; command line when you open a capture file.
  37. ;
  38. ; This version does not use the PSP swapping of previous versions.
  39. ;
  40. ;------------------------------------------------------------
  41. ; Updates for Version 5.02        19 Nov 86
  42. ;
  43. ; Added -i inactivate option. Must be only option on command line :
  44. ;        lptx -i
  45. ;
  46. ; This version adds a check for DOS interuupt 21h
  47. ; function 40h for standard printer device = 0004.
  48. ;
  49. ; This addition was suggested by Dale Letterman of Seattle.
  50. ;
  51. ; Assembled using MicroSoft MASM v 4.0
  52. ;
  53. ; Program is called and used in the same way as version 3.00
  54. ;
  55. ; I now enter DOS with interrupts disabled.
  56. ;
  57. ; Added a switch to inhibit the checking of the Critical Section Flag
  58. ; Add -x to the command line the first time that you run LPTx.
  59. ;
  60. ;------------------------------------------------------------
  61. ; Updates for Version 5.00        13 May 86
  62. ;
  63. ; This version also takes over the DOS interuupt 21h and specifically
  64. ; checks for function 5.
  65. ; If that is the call, LPTx captures the character if LPTx has been
  66. ; activated. If it is a DOS call, LPTx assumes that DOS wants LPT1 since
  67. ; there is no way for the DOS call to specify a line printer number.
  68. ;
  69. ; Uses undocumented DOS int 21h calls 50h and 51h.
  70. ;    50h    Set new current Program Segment Prefix(PSP) from
  71. ;        segment number in BX
  72. ;    51h    Get current PSP into BX.
  73. ; These calls are used before any file is opened by the resident portion
  74. ; of LPTx. There is some concern that DOS puts information about open files
  75. ; into the current PSP. Before we open our spooler file, we want to set
  76. ; the current PSP to our PSP and then restore it after the file I/O
  77. ; is complete. This idea was expressed in PC Magazine May 13, 1986 on page
  78. ; 314 in an article by Charles Petzold.
  79. ;
  80. ;
  81. ; This version 5.0 does not obsolete versions 4.0 and 3.0. Those versions
  82. ; may work under some conditions where this one does not and vica versa.
  83. ;------------------------------------------------------------
  84. ; Updates for Version 4.01        5 May 86
  85. ;
  86. ; Had an error in the way LPTx detected if it was already in memory.
  87. ; This error existed from back in version 3.00 and may have been
  88. ; the cause of this program locking up the system the very first
  89. ; time it was called.
  90. ;------------------------------------------------------------
  91. ; Updates for Version 4.0        25 April 86
  92. ;
  93. ; Assembled using MicroSoft MASM v 4.0
  94. ;
  95. ; Program is called and used in the same way as version 3.00
  96. ;
  97. ; Modified the code to check if DOS was running when the print interrupt
  98. ; occurs. If so the print request is routed back to the regular line
  99. ; printer. This will limit the use of this capture program to user
  100. ; programs which do their own output without going to DOS.
  101. ;
  102. ; In turn, this guarantees that we do re-enter DOS.
  103. ;
  104. ; The trick of saving the DOS stack was dropped in this version and
  105. ; I have resorted to another trick which I garnered from the
  106. ; following message found on info-ibmpc. I use method number 2.
  107. ;
  108. ; This version 4.0 does not obsolete version 3.0. That version may
  109. ; work under some conditions where this one does not and vica versa.
  110. ; This one worked fine for me using DOS 2.1 and 123 version 1.A.
  111. ; Will not work with Shift PrtSc.
  112. ;
  113. comment *
  114. Date: Thu, 30 Jan 86 08:47:51 est
  115. Subject: File I/O from resident programs
  116. To: allegra!seismo!usc-isib.arpa!info-ibmpc
  117.  
  118. Regarding opening up a file when you are terminate-and-stay-resident:
  119.  
  120. Be very careful when you attempt this.  Many an FAT has been eaten for
  121. lunch when I first tried doing it.  Two ways that work like a charm:
  122.  
  123. 1)    Take over interrupt 0x28.  This interrupt gets called by DOS
  124.     while its waiting for a key to be hit. Whenever it does get
  125.     called (your program should not be time critical, btw, as
  126.     this routine is never called from CPU intensive tasks), it
  127.     is safe to do with DOS what you will. (Except for certain
  128.     interruptions, such as Search First and Search Next, which
  129.     either you'll screw-up for the foreground task, or they'll
  130.     screw-up for you.)
  131.  
  132. 2)    Get the Critical Section Flag by issuing an int 21, with ah=0x34.
  133.     This returns a pointer to a flag in ES:BX. When this flag is
  134.     NULL, and interrupts are on, it is safe to play DOS games.
  135.     Unless you are the last program to take over the interrupt,
  136.     don't trust the flag word: many "fine" programs like SideKick
  137.     do not give you a true copy of the flag word on the stack, but
  138.     rather give a simple "pushf" after interrupts are turned off.
  139. * ;end of comment
  140. ;
  141. ; More information from a message posted on USENIX:
  142. ;
  143. comment *
  144. From sdcsvax!ihnp4!timeinc!greenber Mon Jul  1 05:12:16 1985
  145. Date: 30 Jun 85 17:12:37 CDT (Sun)
  146. -----------------------------------------------------------
  147. INT 21 - Internal - Return CritSectFlag Pointer (MSDOS generic)
  148.       REG AH = 34H
  149.       On Return:
  150.             ES:BX points to DOS "Critical Section Flag"
  151.       When byte pointed to is zero, DOS is supposed to be
  152.       safe to interrupt. NOT RELIABLE according to Chris
  153.       Dunford.
  154.             Examination of DOS 2.10 code in this area
  155.       indicates that the byte immediately FOLLOWING this
  156.       "Critical Section Flag" must be 00 to permit the
  157.       PRINT.COM interrupt to be called. This suggests that
  158.       checking the WORD pointed to, rather than the BYTE,
  159.       might increase reliability of the test greatly.
  160. -----------------------------------------------------------
  161. INT 28 - Internal routine for MSDOS
  162.       This interrupt is called from inside the "get input
  163. from keyboard" routine in DOS, if and only if it is safe to use
  164. INT 21 to access the disk at that time. It is used primarily by
  165. the PRINT.COM routines, but any number of other routines could
  166. be chained to it by saving the original vector, and calling it
  167. with a FAR call (or just JMPing to it) at the end of the new
  168. routine.
  169.       Until PRINT.COM installs its own routine, this
  170. interrupt vector simply points to an IRET opcode.
  171. -----------------------------------------------------------
  172. * ;end of comment
  173. ;
  174. ;------------------------------------------------------------
  175. ; Updates for Version 3.0
  176. ;
  177. ; This version is fully compatible with IBM's PRINT command and
  178. ; hopefully most other print spoolers. I changed the method by which
  179. ; LPTx determines if a resident copy of itself is already in memory.
  180. ;
  181. ;------------------------------------------------------------
  182. ; This program intercepts the BIOS interrupt 17, the line printer
  183. ; interrupt. It will redirect the output of LPT1, LPT2, or LPT3 to a disk
  184. ; file. All three redirects may be active at the same time.
  185. ;
  186. ;
  187. ; Background:
  188. ;
  189. ; The basic problem with this type of program is that PC-DOS as written
  190. ; by Microsoft is not re-entrant. That means that if DOS is in control when
  191. ; the print interrupt occurs, you can not call DOS again to do some other
  192. ; function. Therefore, LPTx can not call DOS to write the captured print
  193. ; data to disk. Version 3.00 of LPTx tries to get around this by making
  194. ; PC-DOS re-entrant. Version 4.00 of LPTx gets around this by not ever
  195. ; trying to re-enter DOS.
  196. ;
  197. ;*******This Program Must be Converted to a .COM file before running ******
  198. ; Assemble with :
  199. ;    masm    lptx;
  200. ;    link    lptx;
  201. ;    exe2bin lptx,lptx.com
  202. ;    erase lptx.obj
  203. ;    erase lptx.exe
  204. ;
  205. ;-----------------------------------------------------------------
  206. .list
  207. if1
  208.     %out Pass 1 - Including Macros - v6.00
  209. ;
  210. .xlist
  211. ;-----------------------------------------------------------------
  212. ;
  213. ; Macros
  214. ;
  215. display    macro    msg
  216.     mov    DX,offset msg
  217.     mov    AH,DISPLAY_OUTPUT
  218.     int    DOS_CALL
  219.     endm
  220. ;
  221. ; For the PC-AT:
  222. ; POPF macro described in the IBM Personal Computer
  223. ; Seminar Proceedings Volume 2, Number 4 September 1984
  224. ; QUOTE
  225. ; "If the system microprocessor executes a POPF instruction in either
  226. ; the real or the virtual address mode with CPL <= IOPL, then a 
  227. ; pending maskable interrupt (the INTR pin active) may be improperly
  228. ; recognized after executing the POPF instruction even if maskable
  229. ; interrupts were disabled before the POPF instruction and the value 
  230. ; popped had IF=0. If the interrupt is improperly recognized, the
  231. ; interrupt is still correctly executed. This errata has no effect
  232. ; when interrupts are enabled in either real or virtual address mode.
  233. ; The errata has no effect in the virtual address mode when
  234. ; CPL > IOPL."
  235. ;
  236. popff    macro        ;use POPFF instead of POPF
  237.     local    popem,skip
  238.             ;simulate popping flags using IRET
  239.     jmp    short skip    ;jump around iret
  240. popem:
  241.     iret        ;pop CS,IP,flags
  242. skip:
  243.     push    CS
  244.     call    popem    ;call within segment
  245.             ;program will continue here
  246.     endm
  247.  
  248. ;
  249. call_dos    macro        ;Enter DOS with interrupts disabled
  250.     cli
  251.     int    DOS_CALL
  252.     sti
  253.     endm
  254. ;
  255. ;
  256. .list
  257. else
  258.     %out Pass 2
  259. endif
  260. ;-----------------------------------------------------------------
  261. NULL        equ    0
  262. OFF        equ    0
  263. ON        equ    1
  264. EMPTY        equ    0
  265. BEL        equ    7
  266. CR        equ    13
  267. LF        equ    10
  268. DOLLAR        equ    '$'
  269. COLON        equ    ':'
  270. BACKSLASH    equ    '\'
  271. BLANK        equ    ' '
  272. DASH        equ    '-'
  273. DOS_CALL    equ    21h
  274. ;
  275. REQ        equ    00B90h    ;LPTx request flag
  276. ACK        equ    0ABBBh    ;LPTx acknowledge flag
  277. ;
  278. BUFSIZE        equ    4096    ;size of DMA buffer
  279. DISPLAY_OUTPUT    equ    9    ;for DOS call
  280. DEF_DRIVE    equ    19h
  281. CREATE_FILE    equ    3Ch
  282. OPEN_FILE    equ    3Dh
  283. CLOSE_FILE    equ    3Eh
  284. WRITE_FILE    equ    40h
  285. DELETE_FILE    equ    41h
  286. LSEEK_FILE    equ    42h
  287. DEF_PATH    equ    47h
  288. FIND_FILE    equ    4Eh
  289. ;-----------------------------------------------------------------
  290. ;
  291. p_block    struc
  292. ;
  293. ; data structure - these variables are used only in the
  294. ; memory resident copy of LPTx. BX is set to point to the offset of the
  295. ; allocation of this structure for the selected LPT
  296. ; NOTE : all of the labels in this structure are required as place
  297. ; holders even if not referenced. Used by the initialization calls
  298. ; later on.
  299. ;
  300. active    db    OFF        ;ON = this LPTx is on, OFF = off
  301. handle    dw    NULL        ;handle of disk file used by this LPT
  302. filen    db    'a:\lptx'    ; space for redirection disk file name
  303. pnum    db    BLANK
  304.     db    '.lst',NULL
  305.     db    '                          '
  306.     db    '                        '
  307. bufcntr    dw    EMPTY        ;bytes used in DMA buffer for this LPT
  308. request    db    OFF        ;ON indicates a write request is active
  309.                 ;for this LPT
  310. prt_status    db    10h    ;printer status for this LPT
  311. linefeed    db    ON        ;ON = output a linefeed
  312.                     ;OFF = don't output a linefeed
  313. buffer    db    BUFSIZE dup(0)    ;data buffer for this LPT
  314. ;
  315. p_block    ends
  316. ;
  317. ;-----------------------------------------------------------------
  318. ;
  319. subttl    Main Code
  320. page
  321. %out Assembling CODE Segment
  322. cseg    segment para public 'CODE'
  323.     assume  CS:cseg,DS:nothing,SS:nothing
  324.     org    100h
  325. lptx:    jmp    lptx_start
  326. ;
  327. ; What follows are three allocations of the Structure p_block
  328. ; One for each line printer that we can support.
  329. ; With this, all three line printers have DMA buffers and flag
  330. ; variables.
  331. ; BX is used to point to the offset of the allocation currently in use
  332. ; Line printer 1
  333. lpt1        p_block    <,,,'1'>
  334. ; Line printer 2
  335. lpt2        p_block    <,,,'2'>
  336. ; Line printer 3
  337. lpt3        p_block    <,,,'3'>
  338. ;
  339. crit_flag    db    OFF    ;set to ON if critical error occured
  340. off_crit    dw    0    ;save old critical error address
  341. seg_crit    dw    0
  342. ;
  343. csect_off    dw    0    ;pointer to Critical Section flag
  344. csect_seg    dw    0
  345. ; cs_switch can be cleared by the transient copy of LPTx
  346. cs_switch    db    ON    ;enable check of Critical Section flag
  347. P_NORMAL    equ    90h    ;Printed selected and ready
  348. P_TIMEOUT    equ    01h    ;Time out
  349. save_psp    dw    0    ;Save area for User's PSP Segment Address
  350. lptx_psp    dw    0    ;Our PSP Segment Address
  351. byte_count    dw    0    ;to save DOS byte count
  352. busy        db    OFF    ;ON indicates write is taking place
  353. sound        db    OFF    ;ON uses speaker to indicate progress of LPTx
  354. ; Old interrupt vector addresses
  355. old_08h    dd    0        ;address of old int 08h routine
  356. old_17h    dd    0        ;address of old int 17h routine
  357. old_21h    dd    0        ;address of old int 21h routine
  358. old_28h    dd    0        ;address of old int 28h routine
  359. ; New Stack for Interrupt 17h
  360. stk1_save    dd    0    ;caller's stack EA
  361.         db    128 dup('STACK   ')
  362. stk1        equ    this byte - 4
  363. ; New Stack for Interrupts 08h and 28h
  364. stk2_save    dd    0    ;caller's stack EA
  365.         db    128 dup('STACK   ')
  366. stk2        equ    this byte - 4
  367. ;-----------------------------------------------------------------
  368. ;
  369. ; Interrupt handler for interrupt 17h
  370. ;
  371. int_17h    proc    far
  372.     sti            ;interrupts on
  373.     cmp    AH,3        ;AH=3 for LPTx Function
  374.     jne    reg_call    ;This is a regular print call
  375.     jmp    ret_ack        ;This is an LPTx Call
  376. reg_call:
  377.     push    BX
  378. ; set up BX to point to the data area for the requested printer
  379.     cmp    DX,0        ;lpt1?
  380.     jne    chk_lpt2    ;no
  381.     mov    BX,offset lpt1    ;offset to LPT1
  382.     jmp    short bx_set
  383. chk_lpt2:
  384.     cmp    DX,1        ;lpt2?
  385.     jne    chk_lpt3    ;no
  386.     mov    BX,offset lpt2    ;offset to LPT2
  387.     jmp    short bx_set
  388. chk_lpt3:
  389.     cmp    DX,2        ;lpt3?
  390.     jne    ill_ptr        ;no - bad printer number
  391.     mov    BX,offset lpt3    ;offset to LPT3
  392. bx_set:    
  393.     cmp    CS:[BX].active,OFF    ;are we active?
  394.     je    sleep17        ;no
  395.     mov    CS:[BX].prt_status,P_NORMAL    ;signal ready status
  396.     cmp    AH,1        ;initialize call?
  397.     je    do_nix        ;yes    
  398.     cmp    AH,2        ;status call?
  399.     je    do_nix        ;yes
  400.     cmp    AH,0        ;print call?
  401.     jne    do_nix        ;no
  402.  
  403.     jmp    prt_17        ;we are active
  404. do_nix:    mov    AH,CS:[BX].prt_status    ;return print status
  405. rtn:    pop    BX
  406.     iret
  407. ;
  408. ill_ptr:mov    CS:[BX].prt_status,P_TIMEOUT    ;time out status
  409.     jmp    do_nix
  410. ;
  411. ret_ack:            ;return acknowledgement that I'm here
  412. ; note : Change REQ & ACK from version to version so two versions
  413. ; of LPTx don't get intermixed.
  414.     cmp    DX,REQ        ;my flag to detect that LPTx is
  415.                 ;already loaded and alive.
  416.     jne    ret_nak        ;return a NAK
  417.     mov    DX,ACK        ;Memory resident LPTx answers with ACK
  418.     push    CS        ;now set up ES to point to the resident
  419.     pop    ES        ; data area
  420. ret_nak:
  421.     iret            ;return to calling program
  422. ;
  423. sleep17:pop    BX        ;restore BX before we go to sleep
  424.     jmp    dword ptr CS:old_17h    ;jump immediate to original handler
  425. ;
  426. prt_17:
  427.     push    AX        ; Start the print process.
  428.     push    BX        ; Character is in AL.
  429.     push    CX
  430.     push    DX
  431.     push    DS
  432.     push    ES
  433.     push    SI
  434.     push    DI
  435.     push    BP
  436. ;
  437.     push    CS        ; DS is used as the segment register
  438.     pop    DS        ; for all data during the interrupt
  439. ;
  440.     pushf
  441.     cli
  442.     mov    SI,SS
  443.     mov    word ptr DS:stk1_save+2,SI    ;save caller's stack pointer
  444.     mov    SI,SP
  445.     mov    word ptr DS:stk1_save,SI
  446.     mov    SI,CS
  447.     mov    SS,SI            ;give me new bigger stack
  448.     mov    SI,offset stk1
  449.     mov    SP,SI
  450.     popff
  451.     mov    DS:[BX].prt_status,P_NORMAL    ;signal ready status
  452.                     ;prt_status is set before
  453.                     ;the call to prnt so that prnt
  454.                     ;can change it if the print to
  455.                     ;disk fails
  456.     call    prnt            ;print the character
  457. ;
  458.     pushf
  459.     cli
  460.     mov    SI,word ptr DS:stk1_save
  461.     mov    SP,SI            ;restore caller's stack pointer
  462.     mov    SI,word ptr DS:stk1_save+2
  463.     mov    SS,SI
  464.     popff
  465.  
  466.     pop    BP
  467.     pop    DI
  468.     pop    SI
  469.     pop    ES
  470.     pop    DS
  471.     pop    DX
  472.     pop    CX
  473.     pop    BX
  474.     pop    AX
  475.     jmp    do_nix
  476. int_17h    endp
  477. ;-----------------------------------------------------------------
  478. ;
  479. ; Interrupt handler for interrupt 21h
  480. ;
  481. int_21h    proc    far
  482.     sti            ;interrupts on
  483.     push    BX
  484.     cmp    AH,5        ;is this a DOS printer call?
  485.     je    int_21h_5    ;yes
  486.     cmp    AH,40h        ;is this a DOS write call?
  487.     je    int_21h_40    ;yes
  488.     jmp    sleep21        ;no - go on to real DOS
  489. ; DOS Function 40h - Write to File or Device. DS:DX contains the address
  490. ;of data to write. CX contains the byte count. Return AX = byte count.
  491. int_21h_40:                
  492.     cmp    BX,0004            ;Standard Printer?
  493.     jne    sleep21            ;no - go on to real DOS
  494. ; set up BX to point to the data area for the requested printer
  495. ; Since we don't know what the standard printer device is, we
  496. ; use LPT1
  497.     mov    BX,offset lpt1    ;offset to LPT1
  498.     cmp    CS:[BX].active,OFF    ;are we active?
  499.     je    sleep21            ;no
  500.     push    AX        ;now do it
  501.     push    BX
  502.     push    CX
  503.     push    DX
  504.     mov    CS:byte_count,CX    ;save byte count
  505.     mov    BX,DX
  506. ;DS:BX points to buffer, CX has byte count
  507.     cmp    CX,EMPTY        ;check for zero byte count
  508.     je    prt_21_done
  509. loop21_40:    
  510.     mov    AL,[BX]        ;get a character
  511.     mov    AH,0        ;set up for call to interrupt 17h
  512.     int    17h        ;note : int17h returns a printer status
  513.                 ;in AH but DOS does not define a way
  514.                 ;to return that status
  515.     inc    BX
  516.     loop    loop21_40
  517. prt_21_done:
  518.     pop    DX
  519.     pop    CX
  520.     pop    BX
  521.     pop    AX
  522.     mov    AX,CS:byte_count;DOS returns byte count
  523.     jmp    exit21        ;to return it to the user.
  524. ;
  525. int_21h_5:
  526. ; DOS Function 5 Printer Output. The Character in DL is output to the
  527. ; standard printer device.
  528. ;
  529. ; set up BX to point to the data area for the requested printer
  530. ; Since we don't know what the standard printer device is, we
  531. ; use LPT1
  532.     mov    BX,offset lpt1    ;offset to LPT1
  533.     cmp    CS:[BX].active,OFF    ;are we active?
  534.     je    sleep21            ;no
  535.  
  536.     push    AX
  537.     mov    AH,0        ;set up for call to interrupt 17h
  538.     mov    AL,DL        ;the character
  539.     int    17h
  540.     pop    AX
  541.                 ;note : int17h returns a printer status
  542.                 ;in AH but DOS does not define a way
  543.                 ;to return it to the user.
  544. exit21:    pop    BX
  545.     popff            ;restore flags
  546.     clc            ;never an error from us
  547.     ret            ;return to caller (regular FAR return)
  548. ;
  549. sleep21:pop    BX        ;restore BX before we go to sleep
  550.     jmp    dword ptr CS:old_21h
  551. int_21h    endp
  552. ;
  553. ;-----------------------------------------------------------------
  554. ;
  555. ; PRNT - Print a character in AL
  556. ;
  557. prnt    proc    near
  558.     push    DS
  559.     cmp    DS:[BX].active,OFF
  560.     je    prtext            ;nothing there?
  561.     push    AX
  562.     cmp    DS:[BX].bufcntr,BUFSIZE/2    ;buffer half full?
  563.     jne    intadd            ;no
  564. ;
  565. ; set write request but don't actually write anything out.
  566. ; we hope that the write can take place before the buffer fills up
  567. ;
  568.     mov    DS:[BX].request,ON
  569.     cmp    DS:sound,OFF        ;sound on?
  570.     je    intadd            ;no
  571.     call    horn            ;sound horn to indicate buffer
  572.                     ; write request has been made
  573. ;
  574. intadd:    pop    AX
  575.     cmp    AL,LF            ;is it a linefeed?
  576.     jne    intnolf            ;no
  577.     cmp    DS:[BX].linefeed,OFF    ;we are stripping linefeeds?
  578.     je    prtext            ;yes
  579. intnolf:
  580.     mov    DI,BX            ;offset of this printer's allocation
  581.     add    DI,offset buffer    ;add in offset of buffer
  582.     add    DI,DS:[BX].bufcntr    ;add in current byte count
  583.     mov    DS:[DI],AL        ;stuff it
  584.     inc    DS:[BX].bufcntr
  585.     cmp    DS:[BX].bufcntr,BUFSIZE    ;buffer overflow?
  586.     jne    prtext            ;no
  587.     mov    DS:[BX].active,OFF    ;yes, nothing to do but deactivate
  588.                     ; LPTx
  589.     cmp    DS:sound,OFF        ;sound on?
  590.     je    prtext            ;no
  591.     call    beep            ;sound beep twice to indicate that
  592.     call    beep            ;we have been deactivated
  593. prtext:    pop    DS
  594.     ret                ;done
  595. prnt    endp
  596. ;-----------------------------------------------------------------
  597. ;
  598. ; Critical Error Handler
  599. ;
  600. crit_int    proc    far        ;got critical error
  601.     mov    CS:crit_flag,ON        ; set flag
  602.     mov    AL,0            ;tells DOS to ignore the
  603.     iret                ;error
  604. crit_int    endp
  605. ;-----------------------------------------------------------------
  606. ;
  607. ; Interrupt handler for interrupt 08h - clock ticks
  608. ;
  609. ; This function is installed as a handler for hardware interrupt
  610. ; type 8. It first calls the previous int 08h handler to service
  611. ; the INTEL 8259 Programmable Interrupt Controller. Then it checks
  612. ; to see if any write requests are pending. If so, it calls do_save
  613. ; and flush to write the buffer to the disk. Note that int_08h
  614. ; checks the DOS critical section flag : do_save is called only if
  615. ; DOS is available.
  616. ;
  617. ; This function protects itself against secondary invocations by
  618. ; means of the global busy flag.
  619. ;
  620. int_08h    proc    far
  621. ; call original int 8h handler - ALWAYS
  622.     pushf
  623.     call    dword ptr CS:old_08h
  624. ; now we can process if possible
  625.     pushf
  626.     cli
  627.     cmp    CS:busy,OFF        ;can we process this?
  628.     jne    i08_exit        ;no
  629. ; if DOS is in its critical section, we skip the write for now and
  630. ; hope that we can write before the buffer fills
  631.     cmp    CS:cs_switch,OFF    ;is checking off?
  632.     je    no_cs            ;yes, don't check the flag
  633.     push    DS            ; check the critical section flag
  634.     push    SI
  635.     lds    SI,dword ptr CS:csect_off
  636.     cmp    byte ptr [SI],OFF
  637.     pop    SI
  638.     pop    DS
  639.     jne    i08_exit        ;DOS in critical section
  640.                     ;this indicates that we cannot
  641.                     ;do any disk operations at this
  642.                     ;time
  643. no_cs:
  644. ; set busy flag
  645.     mov    CS:busy,ON
  646.     call    do_save
  647.     mov    CS:busy,OFF
  648. i08_exit:
  649.     popff
  650.     iret
  651. int_08h    endp
  652. ;-----------------------------------------------------------------
  653. ;
  654. ; Interrupt handler for interrupt 28h - idle
  655. ;
  656. ; This function is installed as a handler for hardware interrupt
  657. ; type 28h. It first calls the previous int 28h handler. Then it checks
  658. ; to see if any write requests are pending. If so, it calls do_save
  659. ; and flush to write the buffer to the disk. It does not check the
  660. ; Critical Section Flag as int 28h handlers are allowed to perform
  661. ; DOS disk I/O but not keyboard I/O since most of the time int 28h
  662. ; is called by the keyboard I/O routines when they are waiting
  663. ; for a key press.
  664. ;
  665. ; This function protects itself against secondary invocations by
  666. ; means of the global busy flag.
  667. ;
  668. int_28h    proc    far
  669. ; call original int 28h handler - ALWAYS
  670.     pushf
  671.     call    dword ptr CS:old_28h
  672. ; now we can process if possible
  673.     pushf
  674.     cli
  675.     cmp    CS:busy,OFF        ;can we process this?
  676.     jne    i28_exit        ;no
  677. ; set busy flag
  678.     mov    CS:busy,ON
  679.     call    do_save
  680.     mov    CS:busy,OFF
  681. i28_exit:
  682.     popff
  683.     iret
  684. int_28h    endp
  685. ;------------------------------------------------------------
  686. ;
  687. ; Writes buffer to disk
  688. ;
  689. do_save    proc    near
  690.     push    AX        ; Start the print process.
  691.     push    BX        ; Character is in AL.
  692.     push    CX
  693.     push    DX
  694.     push    DS
  695.     push    ES
  696.     push    SI
  697.     push    DI
  698.     push    BP
  699.     pushf
  700.     sti            ;interrupts on
  701. ;
  702.     push    CS        ; DS is used as the segment register
  703.     pop    DS        ; for all data during the interrupt
  704. ;
  705.     pushf
  706.     cli
  707.     mov    SI,SS
  708.     mov    word ptr DS:stk2_save+2,SI    ;save caller's stack pointer
  709.     mov    SI,SP
  710.     mov    word ptr DS:stk2_save,SI
  711.     mov    SI,CS
  712.     mov    SS,SI            ;give me new bigger stack
  713.     mov    SI,offset stk2
  714.     mov    SP,SI
  715.     popff
  716. ;
  717. ; check to see if any write requests are active.
  718. ; we do not look at the active flag since the print redirection
  719. ; may have been inactivated by the time, we are able to write to
  720. ; the disk.
  721. ;
  722. ; set up BX to point to the data area for the requested printer
  723.     mov    BX,offset lpt1    ;offset to LPT1
  724.     cmp    DS:[BX].request,ON    ;write LPT1 request?
  725.     jne    c28_lpt2        ;no
  726.     call    flush
  727.     mov    DS:[BX].request,OFF
  728. c28_lpt2:
  729.     mov    BX,offset lpt2    ;offset to LPT2
  730.     cmp    DS:[BX].request,ON    ;write LPT2 request?
  731.     jne    c28_lpt3        ;no
  732.     call    flush
  733.     mov    DS:[BX].request,OFF
  734. c28_lpt3:
  735.     mov    BX,offset lpt3    ;offset to LPT3
  736.     cmp    DS:[BX].request,ON    ;write LPT3 request?
  737.     jne    i28_done        ;no
  738.     call    flush
  739.     mov    DS:[BX].request,OFF
  740. i28_done:
  741.     pushf
  742.     cli
  743.     mov    SI,word ptr DS:stk2_save
  744.     mov    SP,SI            ;restore caller's stack pointer
  745.     mov    SI,word ptr DS:stk2_save+2
  746.     mov    SS,SI
  747.     popff
  748. ;
  749.     popff
  750.     pop    BP
  751.     pop    DI
  752.     pop    SI
  753.     pop    ES
  754.     pop    DS
  755.     pop    DX
  756.     pop    CX
  757.     pop    BX
  758.     pop    AX
  759.     ret
  760. do_save    endp
  761. ;
  762. ;------------------------------------------------------------
  763. ;
  764. ; FLUSH - Flush print buffer to disk file
  765. ;
  766. flush    proc    near
  767.     cmp    DS:[BX].bufcntr,EMPTY    ;buffer empty?
  768.     jne    flush_buf        ;no, write it to disk
  769.     ret                ;exit
  770. flush_buf:
  771.     call    disk_out
  772.     ret
  773. flush    endp
  774. ;------------------------------------------------------------
  775. ;
  776. ; DISK_OUT - write to disk
  777. ;
  778. disk_out    proc    near
  779. ;PSP
  780. ;    push    BX
  781. ;    mov    AH,51h            ;get current PSP
  782. ;    call_dos
  783. ;    mov    DS:save_psp,BX        ;and save it
  784. ;    mov    BX,DS:lptx_psp        ;get our PSP
  785. ;    mov    AH,50h
  786. ;    call_dos        ;set it into DOS
  787. ;    pop    BX
  788. ;
  789.     push    ES
  790.     push    DS
  791.     mov    AX,DS            ;set up ES
  792.     mov    ES,AX
  793.     push    BX
  794.     push    ES
  795.     mov    AX,3524h        ;get old critical error vector
  796.     call_dos
  797.     mov    DS:off_crit,BX
  798.     mov    DS:seg_crit,ES
  799.     mov    DX,offset crit_int
  800.     mov    AX,2524h    
  801.     call_dos            ;trap critical error vector
  802.     mov    DS:crit_flag,OFF    ;clear critical error flag
  803.     pop    ES
  804.     pop    BX
  805.     mov    DX,BX            ;open file
  806.     add    DX,offset filen        ;filename
  807.     mov    AL,1            ;open for writing
  808.     mov    AH,open_FILE
  809.     call_dos
  810.     mov    DS:[BX].handle,AX    ;file handle
  811.     jc    disk_err        ;error
  812.     cmp    DS:crit_flag,ON        ;critical error?
  813.     je    disk_err        ;yes
  814.     push    BX
  815.     mov    AH,lseek_FILE
  816.     mov    AL,2            ;end of file
  817.     mov    CX,0            ;offset 0
  818.     mov    DX,0
  819.     mov    BX,DS:[BX].handle
  820.     call_dos
  821.     pop    BX
  822.     jc    disk_err        ;some seek error
  823.     cmp    DS:crit_flag,ON        ;critical error?
  824.     je    disk_err        ;yes
  825.     mov    CX,DS:[BX].bufcntr    ;buffer size
  826.     mov    DX,BX            ;offset of structure allocation
  827.     add    DX,offset buffer    ;add offset of buffer within the
  828.                     ;    allocation
  829.     push    BX
  830.     mov    AH,write_FILE
  831.     mov    BX,DS:[BX].handle    ;file handle
  832.     call_dos            ;buffer address is DS:DX
  833.     pop    BX
  834.     jnc    disk_ok
  835.     cmp    DS:crit_flag,ON        ;critical error?
  836.     je    disk_err        ;yes
  837.     cmp    AX,DS:[BX].bufcntr    ;did DOS write it all?
  838.     je    disk_ok            ;yes
  839. disk_err:
  840.     call    beep            ;ring bell 4 times
  841.     call    beep
  842.     call    beep
  843.     call    beep
  844.     mov    DS:[BX].active,OFF    ;turn us off
  845.     mov    DS:crit_flag,OFF    ;clear error flag
  846.     mov    DS:[BX].prt_status,P_TIMEOUT    ;signal time out error
  847.                     ;then try to close the file
  848.                     ;to save what we can
  849.     jmp    disk_close
  850. disk_ok:
  851.     mov    DS:[BX].bufcntr,EMPTY    ;set buffer empty
  852. disk_close:
  853.     push    BX
  854.     mov    BX,DS:[BX].handle
  855.     mov    AH,close_FILE        ;close the file
  856.     call_dos
  857.     pop    BX
  858. disk_exit:
  859.     pop    DS
  860.     pop    ES
  861.     push    DS
  862.     lds    DX,dword ptr DS:off_crit
  863.     mov    AX,2524h        ;restore critical error vector
  864.     call_dos
  865.     pop    DS
  866. ; PSP
  867. ;    push    BX
  868. ;    mov    BX,DS:save_psp        ;get user's PSP
  869. ;    mov    AH,50h
  870. ;    call_dos        ;set it back into DOS
  871. ;    pop    BX
  872. ;
  873.     ret
  874. disk_out    endp
  875. ;
  876. ;--------------------------------------------------------------
  877. ; Routine to sound the beeper
  878. ;
  879. note        equ    0a98h    ;2712 = 1193180. / 440Hz
  880. beep    proc    near
  881.     push    AX
  882.     push    BX
  883.     push    CX
  884.     push    DX
  885.     mov    AL,0b6h        ;select tim 2,lsb,msb,binary
  886.     out    43h,AL        ;set up timer chip
  887.     mov    AX,note        ;get note
  888.     out    42h,AL        ;write timer 2 count:  lsb...
  889.     mov    AL,AH
  890.     out    42h,AL        ;...and msb
  891.     in    AL,61h
  892.     mov    AH,AL        ;save current port setting
  893.     or    AL,3
  894.     out    61h,AL        ;turn speaker on
  895.     mov    CX,07FFFh
  896. beep_lp:loop    beep_lp
  897.     mov    AL,AH
  898.     out    61h,AL        ;restore port setting
  899.     mov    CX,03FFFh
  900. beep1_lp:loop    beep1_lp
  901.     pop    DX
  902.     pop    CX
  903.     pop    BX
  904.     pop    AX
  905.     ret            ;return to caller
  906. beep    endp
  907. ;-------------------------------------------------------------------
  908. ; Routine to sound a key click
  909. ;
  910. key_clk        equ    036h    ;59 = 1193180. / 20,000Hz
  911. click    proc    near
  912.     push    AX
  913.     push    BX
  914.     push    CX
  915.     mov    AL,0b6h        ;select tim 2,lsb,msb,binary
  916.     out    43h,AL        ;set up timer chip
  917.     mov    AX,key_clk    ;get note
  918.     out    42h,AL        ;write timer 2 count:  lsb...
  919.     mov    AL,AH
  920.     out    42h,AL        ;...and msb
  921.     in    AL,61h
  922.     mov    AH,AL        ;save current port setting
  923.     or    AL,3
  924.     out    61h,AL        ;turn speaker on
  925.     mov    CX,0FFh
  926. key_lp:loop    key_lp
  927.     mov    AL,AH
  928.     out    61h,AL        ;restore port setting
  929.     mov    CX,0FFh
  930. key1_lp:loop    key1_lp
  931.     pop    CX
  932.     pop    BX
  933.     pop    AX
  934.     ret            ;return to caller
  935. click    endp
  936. ;--------------------------------------------------------------
  937. ; Routine to honk the horn
  938. ;
  939. horn    proc    near
  940.     push    AX
  941.     push    BX
  942.     push    CX
  943.     push    DX
  944.     mov    AL,0b6h        ;select tim 2,lsb,msb,binary
  945.     out    43h,AL        ;set up timer chip
  946.     mov    AX,2e9bh    ;2712 = 1193180. / 100Hz
  947.     out    42h,AL        ;write timer 2 count:  lsb...
  948.     mov    AL,AH
  949.     out    42h,AL        ;...and msb
  950.     in    AL,61h
  951.     mov    AH,AL        ;save current port setting
  952.     or    AL,3
  953.     out    61h,AL        ;turn speaker on
  954.     mov    CX,07FFFh
  955. horn_lp:loop    horn_lp
  956.     mov    AL,AH
  957.     out    61h,AL        ;restore port setting
  958.     mov    CX,03FFFh
  959. horn1_lp:loop    horn1_lp
  960.     pop    DX
  961.     pop    CX
  962.     pop    BX
  963.     pop    AX
  964.     ret            ;return to caller
  965. horn    endp
  966. ;--------------------------------------------------------------------
  967. end_res    db    0
  968. ;
  969. ; This is the end of the memory resident portion of LPTx
  970. ;
  971. ;--------------------------------------------------------------------
  972. ;
  973. ; All of the following data is in the Code Segment
  974. ;
  975. mach_type    db    0
  976. DOS_version    db    0        ;Major Version Number
  977.         db    0        ;Minor Version Number
  978. lfeed        db    ON        ;linefeed enable switch
  979. drive        db    0        ;default drive number 0=A etc.
  980. flag_27        db    OFF        ; 1=make this copy resident
  981. wrong_dos    db    'DOS 2.0 or later required for LPTx',LF,CR,DOLLAR
  982. up_msg        db    'LPTx - Line Printer Redirection Program - V6.00'
  983.         db    LF,CR,'   Copyright 1987 Mark C. DiVecchio',LF,CR
  984.         db    DOLLAR
  985. lptx_resident    db    LF,CR,'Resident Portion of LPTx Loaded',LF,LF,CR
  986.         db    DOLLAR
  987. lptx_err_3    db    'Could not delete file',LF,CR,DOLLAR
  988. lptx_over    db    CR,LF,'File already exists. Do you want to overwrite '
  989.         db    'it? (y or n)  :',DOLLAR
  990. lptx_nc        db    'File selection canceled',CR,LF,DOLLAR
  991. lptx_lf        db    'Stripping Linefeed Characters',CR,LF,DOLLAR
  992. lptx_sd_on    db    'Sound Enabled',CR,LF,DOLLAR
  993. lptx_cs_off    db    'Critical Section Checking Disabled',CR,LF,DOLLAR
  994. lptx_del    db    'File is being overwritten',LF,CR,DOLLAR
  995. lptx_cr        db    LF,CR,DOLLAR
  996. lptx_bad    db    'Invalid Option',LF,CR
  997.         db    'Calling sequence:',LF,CR
  998.         db    'lptx [?] [-x] [-l] [-i] {-1,-2,-3} {-c -o <d:[pathname]filename>}'
  999.         db    LF,CR,DOLLAR
  1000. lptx_on        db    LF,CR,'Redirection started. Disk file opened.'
  1001.         db    LF,CR,DOLLAR
  1002. lptx_off    db    LF,CR,'Redirection ended. Disk file closed.'
  1003.         db    LF,CR,DOLLAR
  1004. lptx_creat    db    'Could not create the disk file',LF,CR,DOLLAR
  1005. lptx_gone    db    LF,'LPTx - Resident portion inactivated',CR,LF,DOLLAR
  1006. ; HELP screen
  1007. help_msg    db    LF,CR,'Calling sequence : ',LF,LF,CR
  1008.         db    'LPTx [?] [-x] [-l] [-i] -p -f <[d:][\pathname\pathname]filename>'
  1009.         db    LF,LF,CR
  1010.         db    '    where  p = printer number : 1, 2, or 3',LF,CR
  1011.         db    '           f = function : o for open a print file'
  1012.         db    LF,CR
  1013.         db    '                          c for close a print file'
  1014.         db    LF,CR
  1015.         db    '           drive letter & pathname are optional'
  1016.         db    LF,CR
  1017.         db    '           x = disable check of Critical Section Flag',
  1018.         db    LF,CR
  1019.         db    '           l = strip Linefeed characters from output',
  1020.         db    LF,CR
  1021.         db    '           i = inactivate LPTx',LF,CR
  1022.         db    '    defaults : p = 1',LF,CR
  1023.         db    '               f = o',LF,CR
  1024.         db    DOLLAR
  1025. ;
  1026. stat_stat    db    CR,LF,'LPTx Status :',CR,LF,DOLLAR
  1027. stat_lp        db    'lpt'
  1028. stat_ptr    db    ' : ',DOLLAR
  1029. stat_off    db    ' not redirected',CR,LF,DOLLAR
  1030. stat_dir    db    ' redirected to disk file '
  1031. stat_fn        db    60 dup (BLANK)
  1032. ;
  1033. yn_max        db    2    ;max # of char
  1034. yn_act        db    0
  1035. yn_in        db    2 dup (0)
  1036. ;
  1037. ;--------------------------------------------------------------------
  1038. ;
  1039. ; This is the main routine which is executed each time that LPTx is 
  1040. ; called. In this routine, DS points to the data segment which is
  1041. ; transient. ES points to the data segment which is permanently
  1042. ; resident. ES:BX points to the data structure for the selected 
  1043. ; line printer, 1, 2, or 3.
  1044. ; The offsets are the same for both. If this is the first
  1045. ; time that LPTx is run, then ES=DS.
  1046. ;
  1047. lptx_start:
  1048.     push    DS    ;Save DS
  1049.     xor    AX,AX    ;clear AX for return IP
  1050.     push    AX    ;put 0 on stack
  1051. ;
  1052. ;to check for machine type look at
  1053. ; F000:FFFE                ; I don't use this currently
  1054. ;    = FF    IBM PC
  1055. ;    = FE    IBM XT or Portable
  1056. ;    = FD    IBM PCjr
  1057. ;    = FC    IBM PC AT
  1058. ;    = F9    IBM Convertible
  1059.     mov    AX,0F000h
  1060.     mov    ES,AX
  1061.     mov    BX,0FFFEh
  1062.     mov    CL,ES:[BX]    ;get machine type
  1063.     mov    mach_type,CL    ;save machine type
  1064. ;
  1065. ;PSP
  1066.     mov    lptx_psp,DS    ;put away our PSP Segment address
  1067. ;
  1068. ; get the DOS version number
  1069. ; returns zero for pre DOS 2.0 releases
  1070.     mov    AH,30h
  1071.     int    DOS_CALL    ;call DOS
  1072.     mov    word ptr DOS_version,AX    
  1073. ; Requires at least version 2.0 and may or may not work
  1074. ; with versions above 2.1
  1075.     cmp    DOS_version,2    ;is it DOS 2.+
  1076.     jge    dos_ok        ;yes
  1077.     display    wrong_dos    ;print error message
  1078.     mov    AH,0
  1079.     int    DOS_CALL    ;terminate
  1080. dos_ok:    mov    AH,def_drive    ;get current default drive
  1081.     int    DOS_CALL
  1082.     mov    drive,AL    ;save the drive number
  1083.     display    up_msg        ;print program ID
  1084.     mov    flag_27,OFF    ;to not make resident
  1085. ; is a copy of LPTx already resident in memory?
  1086.     mov    DX,REQ        ;check if LPTx is already resident
  1087.     mov    AH,3        ;get status - special call
  1088.     int    17h        ;call int 17h - BIOS
  1089.     cmp    DX,ACK        ;my handler sets DX to ACK
  1090.                 ;and sets ES 
  1091.     je    in_core        ;LPTx is resident - ES loaded with
  1092.                 ; segment address
  1093.     mov    flag_27,ON    ;to make this copy resident
  1094.     push    CS
  1095.     pop    ES        ;set ES to CS for segment address
  1096.     mov    AL,drive
  1097.     add    AL,'a'        ;make it a letter
  1098.     mov    BX,offset lpt1
  1099.     mov    ES:[BX].filen,AL    ;put it into the filename
  1100.     mov    BX,offset lpt2
  1101.     mov    ES:[BX].filen,AL    ;put it into the filename
  1102.     mov    BX,offset lpt3
  1103.     mov    ES:[BX].filen,AL    ;put it into the filename
  1104. ; ----------------------------------------------------
  1105. in_core:                ;ES is ok
  1106. ; ES now points to resident data area
  1107. ; set up ES:BX to point to default data structure
  1108.     mov    BX,offset lpt1        ;offset - default to LPT1
  1109. ;get options and file name
  1110. ;scan input line for line printer number
  1111.     mov    SI,81h            ;starting offset
  1112.     mov    CL,DS:80h        ;length of input line
  1113.     mov    CH,0
  1114.     cmp    CX,0            ;nothing?
  1115.     jne    inp_lp            ;no
  1116.     jmp    make_res        ;yes, then just make LPTx resident
  1117.                     ;if it isn't already
  1118. inp_lp:
  1119.     cmp    byte ptr DS:[SI],'?'    ;a ?  ?
  1120.     jne    cont_scan        ;no
  1121.     jmp    help            ;yes - go show help data
  1122. cont_scan:
  1123.     cmp    byte ptr DS:[SI],dash    ;a dash ?
  1124.     je    got_opt            ;yes
  1125.     cmp    byte ptr DS:[SI],CR    ;a carriage return?
  1126.     je    scan_done        ;yes
  1127.     cmp    byte ptr DS:[SI],BLANK    ;a blank?
  1128.     je    inp_ret            ;yes
  1129.     jmp    no_b            ;assume that we got a file name
  1130.                     ;without the -o option
  1131. inp_ret:
  1132.     inc    SI            ;ignore blanks
  1133.     loop    cont_scan            ;continue to scan
  1134. ;
  1135. ; Scan of whole line is complete without a "-o", "-c" or filename
  1136. scan_done:
  1137.     jmp    make_res
  1138. ;
  1139. got_opt:                ;we got an option
  1140.     inc    SI            ;to option
  1141.     cmp    byte ptr DS:[SI],'1'    ;LPT1?
  1142.     jne    chk_2
  1143.     mov    BX,offset lpt1        ;offset from ES
  1144.     jmp    short inp_ret
  1145. chk_2:    cmp    byte ptr DS:[SI],'2'    ;LPT2?
  1146.     jne    chk_3
  1147.     mov    BX,offset lpt2        ;offset from ES
  1148.     jmp    short inp_ret
  1149. chk_3:    cmp    byte ptr DS:[SI],'3'    ;LPT3?
  1150.     jne    chk_fil
  1151.     mov    BX,offset lpt3        ;offset from ES
  1152.     jmp    short inp_ret
  1153. chk_fil:                ;is it file?
  1154.     cmp    byte ptr DS:[SI],'o'    ;open a file
  1155.     je    file_op            ;yes
  1156.     cmp    byte ptr DS:[SI],'c'    ;close a file
  1157.     je    file_cl            ;yes
  1158.     cmp    byte ptr DS:[SI],'x'    ;inhibit check for Critical Section?
  1159.     je    cs_check_off        ;yes
  1160.     cmp    byte ptr DS:[SI],'l'    ;linefeed switch ?
  1161.     je    lf_off            ;yes
  1162.     cmp    byte ptr DS:[SI],'s'    ;enable sound?
  1163.     je    sound_on        ;yes
  1164.     cmp    byte ptr DS:[SI],'i'    ;inactivate?
  1165.     jne    bad_opt
  1166.     jmp    inactivate
  1167. bad_opt:
  1168.     display    lptx_bad        ;incorrect option
  1169.     jmp    nor_ex
  1170. ;
  1171. ; [-x]
  1172. ; turn OFF Critical Section check
  1173. ;
  1174. cs_check_off:
  1175.     mov    ES:cs_switch,OFF
  1176.     display    lptx_cs_off
  1177.     jmp    inp_ret
  1178. ;
  1179. ;[-l]
  1180. ; Turn linefeeds off in captured file
  1181. ;
  1182. lf_off:    mov    lfeed,OFF        ;turn LFs off
  1183.     display    lptx_lf
  1184.     jmp    inp_ret            ;continue the scan
  1185. ;
  1186. ; [-s]
  1187. ; turn ON Sound
  1188. ;
  1189. sound_on:
  1190.     mov    ES:sound,ON
  1191.     display    lptx_sd_on
  1192.     jmp    inp_ret
  1193. ;
  1194. ; [-c]
  1195. ; close output file
  1196. ;
  1197. file_cl:cmp    ES:[BX].active,ON    ;are we active?
  1198.     jne    no_close        ;no
  1199.     mov    AL,1AH            ;CTRL-Z
  1200.     push    DS
  1201.     push    ES
  1202.     pop    DS            ;set DS to point to resident
  1203.                     ;data segment
  1204.     call    prnt            ;print end of file mark
  1205.     call    flush
  1206.     pop    DS            ;restore DS
  1207.     mov    ES:[BX].active,OFF    ;make us inactive
  1208.     display lptx_off        ;redirection off message
  1209. no_close:
  1210.     jmp    nor_exit        ;nothing to close so exit
  1211. ;
  1212. ; [-o]
  1213. ; open a file for ouput
  1214. ;
  1215. file_op:                ;get the file name
  1216.     inc    SI            ;to next chracter
  1217.     cmp    byte ptr DS:[SI],BLANK    ;a blank?
  1218.     jne    no_b            ;no
  1219.     inc    SI            ;skip over blank
  1220. no_b:
  1221. ; at this point, we have found a new file name. We close the old
  1222. ; file if one was open
  1223.     cmp    ES:[BX].active,ON    ;are we active?
  1224.     jne    no_cl            ;no
  1225.     mov    AL,1AH            ;CTRL-Z
  1226.     push    DS
  1227.     push    ES
  1228.     pop    DS            ;set DS to point to resident
  1229.                     ;data segment
  1230.     call    prnt            ;print end of file mark
  1231.     call    flush
  1232.     pop    DS            ;restore DS
  1233.     mov    ES:[BX].active,OFF    ;make us inactive
  1234.     display lptx_off        ;redirection off message
  1235. no_cl:    mov    DI,BX            ;base of structure
  1236.     add    DI,offset filen        ;add offset of destination
  1237.     push    SI            ;save pointer to file name
  1238. ; search for a drive letter
  1239.     inc    SI            ;should point to a colon if
  1240.                     ;one is there
  1241.     cmp    byte ptr [SI],COLON    ;?
  1242.     je    got_drive        ;yes
  1243. get_drive:
  1244.     mov    AL,drive        ;get drive letter
  1245.     add    AL,'a'            ;make it a letter
  1246.     mov    ES:[DI],AL        ;put it in file name
  1247.     inc    DI
  1248.     mov    byte ptr ES:[DI],COLON    ;put in a colon
  1249.     inc    DI
  1250.     jmp    path_search
  1251. got_drive:
  1252.     pop    SI            ;move pointer back to start
  1253.     mov    AL,[SI]            ;get the given drive
  1254.     mov    ES:[DI],AL        ;move it
  1255.     sub    AL,'a'            ;make it a number
  1256.     mov    drive,AL        ;save the drive number
  1257.     inc    SI
  1258.     inc    DI
  1259.     mov    byte ptr ES:[DI],COLON
  1260.     inc    DI
  1261.     inc    SI
  1262.     push    SI            ;save new start pointer
  1263. path_search:
  1264. ; now search for a backslash which says that a pathname was given
  1265. bk_s_lp:cmp    byte ptr [SI],BACKSLASH
  1266.     je    got_path        ;a path
  1267.     cmp    byte ptr [SI],CR    ;end of the file name?
  1268.     je    get_path        ;yes with no path
  1269.     inc    SI
  1270.     jmp    short bk_s_lp            ;loop
  1271. get_path:
  1272.     mov    byte ptr ES:[DI],BACKSLASH    ;create the path
  1273.     inc    DI
  1274.     mov    DL,drive        ;the current drive
  1275.     inc    DL            ;bump it for DOS
  1276.     push    DS
  1277.     push    ES
  1278.     pop    DS            ;set up DS for DOS
  1279.     mov    SI,DI            ;set up SI for pathname
  1280.     mov    AH,def_path        ;get current directory
  1281.     int    DOS_CALL        ;path goes into DS:SI
  1282.     pop    DS            ;restore DS
  1283.     cmp    byte ptr ES:[SI],NULL    ;null path?
  1284.     je    null_path        ;yes - root directory
  1285. path_lp:                ;now find the end of the string
  1286.     cmp    byte ptr ES:[SI],NULL    ;null byte marks end of pathname
  1287.     je    end_path        ;now append the file name
  1288.     inc    SI
  1289.     jmp    short path_lp
  1290. end_path:
  1291.     mov    byte ptr ES:[SI],BACKSLASH
  1292.     inc    SI
  1293. null_path:
  1294.     mov    DI,SI            ;DI is destination
  1295. got_path:
  1296.     pop    SI            ;restore source of filename
  1297. ; pick up everything to next blank
  1298. get_lp:
  1299.     mov    AL,DS:[SI]        ;character
  1300.     mov    ES:[DI],AL        ;put it away
  1301.     cmp    AL,CR            ;was it a Carriage Return?
  1302.     je    end_line
  1303.     cmp    AL,BLANK        ;was it a space?
  1304.     je    end_line
  1305.     inc    SI
  1306.     inc    DI
  1307.     jmp    short get_lp        ;no so get next character
  1308. end_line:
  1309.     mov    byte ptr ES:[DI],NULL    ;zero out the CR or blank
  1310.                     ;at the end of the filename
  1311.                     ;it becomes an ASCIIZ string
  1312.     sub    DI,BX            ;now take out the base and
  1313.     cmp    DI,offset filen        ; make sure that we got something
  1314.     jne    lptx_make        ;file name was ok
  1315.     display lptx_creat        ;could not understand the file name
  1316.     jmp    nor_exit        ;don't stay resident
  1317. nor_ex:    jmp    nor_exit
  1318.  
  1319. lptx_make:
  1320. ; default DTA used by Find File is set by DOS to an offset of
  1321. ; 80h into this program's Program Segment Prefix
  1322.     push    DS
  1323.     push    ES
  1324.     pop    DS            ;uses DS:DX
  1325.     mov    DX,BX
  1326.     add    DX,offset filen        ;file name
  1327.     mov    AH,find_FILE
  1328.     mov    CX,0            ;normal files only
  1329.     int    DOS_CALL        ;find first match
  1330.     pop    DS
  1331.     jnc    lptx_d            ;file was found
  1332.     jmp    lptx_create        ;not there - which is ok
  1333. ;file already exists
  1334. lptx_d:    display lptx_over
  1335.     mov    DX,offset yn_max;input buffer
  1336.     mov    AH,0AH
  1337.     int    DOS_CALL
  1338.     cmp    yn_act,0        ;anything typed?
  1339.     display    lptx_cr
  1340.     je    lptx_x            ;no - exit
  1341.     cmp    yn_in,'y'        ;a yes?
  1342.     je    lptx_d_yes        ;yes
  1343.     cmp    yn_in,'Y'        ;a yes?
  1344.     je    lptx_d_yes        ;yes
  1345. lptx_x:    display    lptx_nc
  1346.     jmp    nor_exit    ;all done if we can't overwrite
  1347.                 ;see if we should abort the host
  1348. lptx_d_yes:
  1349.     display lptx_del
  1350.     push    DS
  1351.     push    ES
  1352.     pop    DS            ;uses DS:DX
  1353.     mov    DX,BX
  1354.     add    DX,offset filen        ;file name
  1355.     mov    AH,delete_FILE
  1356.     int    DOS_CALL        ;delete file
  1357.     pop    DS
  1358.     jnc    lptx_create        ;ok its gone
  1359.     display lptx_err_3        ;can't delete it
  1360.     jmp    nor_exit
  1361. lptx_create:                ; create the file
  1362.     push    DS
  1363.     push    ES
  1364.     pop    DS            ;uses DS:DX
  1365.     mov    DX,BX            ;base of this LPT's structure
  1366.     add    DX,offset filen        ;file name
  1367.     mov    AH,create_FILE
  1368.     mov    CX,0            ;normal files only
  1369.     int    DOS_CALL        ;find first match
  1370.     pop    DS
  1371.     jnc    creat_ok
  1372.     display lptx_creat        ;could not create the file
  1373.     jmp    nor_exit        ;don't stay resident
  1374. creat_ok:                ;now close the file
  1375.     push    BX
  1376.     mov    BX,AX            ;AX was loaded by the create file
  1377.                     ;    call
  1378.     mov    AH,close_FILE        ;close the file
  1379.     int    DOS_CALL
  1380.     pop    BX
  1381.     display    lptx_on
  1382. ; set the program up for writing
  1383.     mov    ES:[BX].bufcntr,EMPTY    ;set buffer empty
  1384.     mov    ES:[BX].active,ON    ;set us on
  1385.     mov    AL,lfeed
  1386.     mov    ES:[BX].linefeed,AL    ;save linefeed switch
  1387. make_res:
  1388.     cmp    flag_27,ON        ;make this one resident?
  1389.     je    resident        ;yes
  1390.     jmp    nor_exit        ;no
  1391. ;
  1392. resident:
  1393.     push    ES
  1394.     push    BX
  1395. ; get old interrupt handler addressses
  1396.     mov    AL,17h        ;get current vector address for 17h
  1397.     mov    AH,35h
  1398.     int    DOS_CALL
  1399.     mov    word ptr old_17h,BX
  1400.     mov    word ptr old_17h[2],ES    ;save it for later use
  1401.  
  1402.     mov    AL,DOS_CALL        ;get current vector address for 21h
  1403.     mov    AH,35h
  1404.     int    DOS_CALL
  1405.     mov    word ptr old_21h,BX
  1406.     mov    word ptr old_21h[2],ES    ;save it for later use
  1407.  
  1408.     mov    AL,08h        ;get current vector address for 08h
  1409.     mov    AH,35h
  1410.     int    DOS_CALL
  1411.     mov    word ptr old_08h,BX
  1412.     mov    word ptr old_08h[2],ES    ;save it for later use
  1413.  
  1414.     mov    AL,28h        ;get current vector address for 28h
  1415.     mov    AH,35h
  1416.     int    DOS_CALL
  1417.     mov    word ptr old_28h,BX
  1418.     mov    word ptr old_28h[2],ES    ;save it for later use
  1419.  
  1420. ;
  1421. ; Set LPTx up as the new int 17h interrupt handler
  1422.     mov    AX,2517h        ;set interrupt vector
  1423.     mov    DX,offset int_17h    ;BIOS printer
  1424.     int    DOS_CALL
  1425. ;
  1426. ; Set LPTx up as the new int 21h interrupt handler
  1427. ;    mov    AX,2521h        ;set interrupt vector
  1428. ;    mov    DX,offset int_21h    ;DOS Functions
  1429. ;    int    DOS_CALL
  1430. ;
  1431. ;
  1432. ; Set LPTx up as the new int 08h interrupt handler
  1433.     mov    AX,2508h        ;set interrupt vector
  1434.     mov    DX,offset int_08h    ;Timer
  1435.     int    DOS_CALL
  1436. ;
  1437. ;
  1438. ; Set LPTx up as the new int 28h interrupt handler
  1439.     mov    AX,2528h        ;set interrupt vector
  1440.     mov    DX,offset int_28h    ;Idle
  1441.     int    DOS_CALL
  1442. ;
  1443.     mov    AH,34h
  1444.     int    DOS_CALL        ;Call Special DOS interrupt
  1445.                     ;returns pointer to critical
  1446.                     ;section flag in ES:BX
  1447.                     ;With DOS 2.1, this returns
  1448.                     ;00EC:012D. I used the XRAY
  1449.                     ;program to look at this
  1450.                     ;byte while DOS was running.
  1451.     mov    csect_seg,ES        ;save the pointer
  1452.     mov    csect_off,BX
  1453.     pop    BX
  1454.     pop    ES
  1455.     display lptx_resident        ;resident loaded message
  1456.     call    stat            ;display status
  1457.     mov    DX,offset end_res
  1458.     int    27h            ;terminate but stay resident
  1459. ;
  1460. ; [?]
  1461. ; HELP printer
  1462. ;
  1463. help:    display    help_msg        ;display the HELP screen
  1464.     jmp    short nor_exit
  1465. ;
  1466. ; Normal exit for transient copy of LPTx
  1467. nor_exit:
  1468.     call    stat            ;display status
  1469. bail_out:
  1470.     mov    AH,0
  1471.     int    DOS_CALL        ;terminate
  1472. ;
  1473. ; [-i]
  1474. ; unhook LPTx from interrupt vectors 08h, 17h, 21h, and 28h
  1475. inactivate:
  1476. ;
  1477.     cmp    word ptr ES:old_17h,0    ;is it sill 0?
  1478.     je    bail_out        ;yes, we weren't installed yet
  1479. ; flush all buffers
  1480.     mov    BX,offset lpt1
  1481.     cmp    ES:[BX].active,ON    ;are we active?
  1482.     jne    no_cl1            ;no
  1483.     mov    AL,1AH            ;CTRL-Z
  1484.     push    DS
  1485.     push    ES
  1486.     pop    DS            ;set DS to point to resident
  1487.                     ;data segment
  1488.     call    prnt            ;print end of file mark
  1489.     call    flush
  1490.     pop    DS            ;restore DS
  1491.     mov    ES:[BX].active,OFF    ;make us inactive
  1492.     display lptx_off        ;capturing off message
  1493. ;
  1494. no_cl1:
  1495.     mov    BX,offset lpt2
  1496.     cmp    ES:[BX].active,ON    ;are we active?
  1497.     jne    no_cl2            ;no
  1498.     mov    AL,1AH            ;CTRL-Z
  1499.     push    DS
  1500.     push    ES
  1501.     pop    DS            ;set DS to point to resident
  1502.                     ;data segment
  1503.     call    prnt            ;print end of file mark
  1504.     call    flush
  1505.     pop    DS            ;restore DS
  1506.     mov    ES:[BX].active,OFF    ;make us inactive
  1507.     display lptx_off        ;capturing off message
  1508. ;
  1509. no_cl2:
  1510.     mov    BX,offset lpt3
  1511.     cmp    ES:[BX].active,ON    ;are we active?
  1512.     jne    no_cl3            ;no
  1513.     mov    AL,1AH            ;CTRL-Z
  1514.     push    DS
  1515.     push    ES
  1516.     pop    DS            ;set DS to point to resident
  1517.                     ;data segment
  1518.     call    prnt            ;print end of file mark
  1519.     call    flush
  1520.     pop    DS            ;restore DS
  1521.     mov    ES:[BX].active,OFF    ;make us inactive
  1522.     display lptx_off        ;capturing off message
  1523. no_cl3:
  1524.     push    DS
  1525.     mov    DX,word ptr ES:old_17h    ;original vector, offset
  1526.     mov    AX,word ptr ES:old_17h[2];original vector, segment
  1527.     mov    DS,AX
  1528.     mov    AX,2517h        ;set interrupt vector to DS:DX
  1529.     int    DOS_CALL
  1530.     pop    DS
  1531.  
  1532.     push    DS
  1533.     mov    DX,word ptr ES:old_21h    ;original vector, offset
  1534.     mov    AX,word ptr ES:old_21h[2];original vector, segment
  1535.     mov    DS,AX
  1536.     mov    AX,2521h        ;set interrupt vector to DS:DX
  1537.     int    DOS_CALL
  1538.     pop    DS
  1539.  
  1540.  
  1541.     push    DS
  1542.     mov    DX,word ptr ES:old_08h    ;original vector, offset
  1543.     mov    AX,word ptr ES:old_08h[2];original vector, segment
  1544.     mov    DS,AX
  1545.     mov    AX,2508h        ;set interrupt vector to DS:DX
  1546.     int    DOS_CALL
  1547.     pop    DS
  1548.  
  1549.  
  1550.     push    DS
  1551.     mov    DX,word ptr ES:old_28h    ;original vector, offset
  1552.     mov    AX,word ptr ES:old_28h[2];original vector, segment
  1553.     mov    DS,AX
  1554.     mov    AX,2528h        ;set interrupt vector to DS:DX
  1555.     int    DOS_CALL
  1556.     pop    DS
  1557.  
  1558.     display    lptx_gone        ;inactivated
  1559.     jmp    bail_out
  1560. ;------------------------------------------------------------------------
  1561. ;
  1562. ; displays the status of each of the three line printers
  1563. ;
  1564. stat    proc    near
  1565. ; display each LPTx with a message "not redirected"
  1566. ;            or redirected to <filename>
  1567.     push    AX
  1568.     push    BX
  1569.     push    DX
  1570.     push    SI
  1571.     push    DI
  1572.     display    stat_stat
  1573. stat_1:    mov    BX,offset lpt1        ;first printer
  1574.     mov    stat_ptr,'1'
  1575.     display    stat_lp
  1576.     cmp    ES:[BX].active,ON    ;are we active?
  1577.     je    stat_1_a        ;yes
  1578.     display stat_off
  1579.     jmp    short stat_2
  1580. stat_1_a:
  1581.     mov    SI,BX            ;base
  1582.     add    SI,offset filen        ;offset
  1583.     mov    DI,offset stat_fn
  1584. stat_1_lp:
  1585.     mov    AL,ES:[SI]
  1586.     mov    [DI],AL
  1587.     inc    SI
  1588.     inc    DI
  1589.     cmp    AL,NULL            ;loop till a null byte is found
  1590.     jne    stat_1_lp
  1591.     mov    byte ptr [DI],CR
  1592.     inc    DI
  1593.     mov    byte ptr [DI],LF
  1594.     inc    DI
  1595.     mov    byte ptr [DI],DOLLAR
  1596.     display stat_dir        ;display file name
  1597. stat_2:
  1598.     mov    BX,offset lpt2        ;second printer
  1599.     mov    stat_ptr,'2'
  1600.     display    stat_lp
  1601.     cmp    ES:[BX].active,ON    ;are we active?
  1602.     je    stat_2_a        ;yes
  1603.     display stat_off
  1604.     jmp    short stat_3
  1605. stat_2_a:
  1606.     mov    SI,BX            ;base
  1607.     add    SI,offset filen        ;offset
  1608.     mov    DI,offset stat_fn
  1609. stat_2_lp:
  1610.     mov    AL,ES:[SI]
  1611.     mov    [DI],AL
  1612.     inc    SI
  1613.     inc    DI
  1614.     cmp    AL,NULL            ;loop till a null byte is found
  1615.     jne    stat_2_lp
  1616.     mov    byte ptr [DI],CR
  1617.     inc    DI
  1618.     mov    byte ptr [DI],LF
  1619.     inc    DI
  1620.     mov    byte ptr [DI],DOLLAR
  1621.     display stat_dir        ;display file name
  1622. stat_3:    mov    BX,offset lpt3        ;third printer
  1623.     mov    stat_ptr,'3'
  1624.     display    stat_lp
  1625.     cmp    ES:[BX].active,ON    ;are we active?
  1626.     je    stat_3_a        ;yes
  1627.     display stat_off
  1628.     jmp    short stat_done
  1629. stat_3_a:
  1630.     mov    SI,BX            ;base
  1631.     add    SI,offset filen        ;offset
  1632.     mov    DI,offset stat_fn
  1633. stat_3_lp:
  1634.     mov    AL,ES:[SI]
  1635.     mov    [DI],AL
  1636.     inc    SI
  1637.     inc    DI
  1638.     cmp    AL,NULL            ;loop till a null byte is found
  1639.     jne    stat_3_lp
  1640.     mov    byte ptr [DI],CR
  1641.     inc    DI
  1642.     mov    byte ptr [DI],LF
  1643.     inc    DI
  1644.     mov    byte ptr [DI],DOLLAR
  1645.     display stat_dir        ;display file name
  1646. stat_done:
  1647.     pop    DI
  1648.     pop    SI
  1649.     pop    DX
  1650.     pop    BX
  1651.     pop    AX
  1652.     ret
  1653. stat    endp
  1654. ;
  1655. cseg    ends
  1656. %out EOF
  1657.     end    lptx
  1658.